#ifndef TROYFRAME_H
#define TROYFRAME_H

#include <QFrame>
#include <QLabel>

#include "GUI/nullglcontroller.h"
#include "Core/Robot/Troy/troy.h"
#include "GUI/Troy/troyglview.h"

#include "Core/Animation/robotaction.h"

/**
  * @brief The TroyFrame provides a simple encapsulation of the entire troy visualization and controller model,
  * and some more advanced and customized facilities.
  *
  * For basic use, it's generally enough to simply create one, add it to some other display object, and let it go.
  * To move troy, you can either set or move individual joints, or you can simply load in an entire keyframe. If you
  * will be modifying multiple joints, you can start and end batches of modifications to improve update speed.
  *
  * Additionally, TroyFrame supports mirroring. At present, mirroring is provided by simply remapping right arm joints
  * to left arm joints, but retrieving the current keyframe will always get the appropriate orientation.
  *
  * Finally, a TroyFrame can be used to preview a RobotAction. When an action is given by setAction(), the TroyFrame
  * takes ownership of the action, and links the action to the preview. By default the action will simply loop.
  * When you want to remove the action, you can call takeAction(), which gives ownership back to the caller, and stops
  * the action.
  */
class TroyFrame : public QFrame {
private:
    Q_OBJECT
    NullGLController* _controller;
    Troy* _model;
    TroyGLView* _view;

    RobotAction* _action;
    bool _ownsAction;
    int _actionLoopCount;
    float _troyScale;

    bool _mirrored;
    QLabel* _mirrorLabel;

    QMap<int, float> _keyFrame;
public:
    explicit TroyFrame(QWidget *parent = 0);
    ~TroyFrame();

    /**
      * @brief Gives ownership of the action to the TroyFrame, and begins looping it on the view.
      *
      * @param action
      */
    void setAction(RobotAction* action);
    /**
      * @brief Removes the action from the TroyFrame, and gives ownership back to the caller.
      *
      * Replaces the actions internal object with a NullBot, so that when a new Robot
      * is set to the action, it is automatically deleted. (NullBot is kind of special
      * cased via the isNull() function in the RobotAction class).
      */
    RobotAction* takeAction();

    /**
      * @brief Gets the keyframe associated with the current position of the preview.
      */
    MotionKeyFrame getKeyFrame() const;

    /**
      * @brief Sets the size of the Troy preview inside the frame to a percentage (between 0 and 1) or the total
      * area. It remains centered. This is used by the snapshot recorder in order to demonstrate how close a match
      * has currently been made.
      *
      * @param percent
      */
    void setTroyScale(float percent);

    /**
      * @brief Checks whether mirroring is currently activated.
      */
    bool mirror() const;
    /**
      * @brief Enables or disables mirroring.
      *
      * @param on
      */
    void mirror(bool on);

public slots:
    /**
      * @brief Sets the TroyGLView::ViewOption. The default is a head on frontal view.
      *
      * @param option
      */
    void setView(TroyGLView::ViewOption option);

    /**
      * @brief Sets the angle (in degrees) of a specific joint.
      *
      * @param jointId
      * @param theta
      */
    void setJointAngle(int jointId, float theta);
    /**
      * @brief Moves a specific joint by angle (in degrees).
      *
      * @param jointId
      * @param theta
      */
    void moveJointBy(int jointId, float delta);

    /**
      * @brief Starts a batch of edits, so that intermediate updates are not processed until the end.
      */
    void startBatch();
    /**
      * @brief Ends a batch of edits, and processes updates that were made.
      */
    void endBatch();

    /**
      * @brief Loads a keyframe as the current position and orientation of the preview.
      *
      * @param keyFrame
      */
    void loadKeyFrame(MotionKeyFrame keyFrame);
private:
    void resizeEvent(QResizeEvent *event);
    static int remapJoint(int jointId, bool on = true);
};

#endif // TROYFRAME_H
